package com.fxkj.cloud.drms.application.impl;

import com.fxkj.cloud.dbs.common.constants.DeviceStatusEnum;
import com.fxkj.cloud.dbs.common.model.DeviceAttributeKey;
import com.fxkj.cloud.dbs.common.model.aggregation.*;
import com.fxkj.cloud.dbs.common.model.command.CommandIdValue;
import com.fxkj.cloud.dbs.common.model.command.CommandSet;
import com.fxkj.cloud.dbs.common.model.linkage.DeviceAttributeKeyValue;
import com.fxkj.cloud.dbs.common.model.DeviceBasicInfo;
import com.fxkj.cloud.drms.application.DeviceApplicationService;
import com.fxkj.cloud.dbs.common.model.linkage.KvDTO;
import com.fxkj.cloud.drms.application.model.dto.TsQueryDTO;
import com.fxkj.cloud.drms.application.model.vo.BatchTskvView;
import com.fxkj.cloud.drms.application.model.vo.DeviceEnergyModelView;
import com.fxkj.cloud.drms.application.model.vo.ProductEnergyStatisticsModelView;
import com.fxkj.cloud.drms.application.model.vo.TskvView;
import com.fxkj.cloud.drms.domain.device.repository.*;
import com.fxkj.cloud.drms.domain.mqtt.CommandDomain;
import com.fxkj.cloud.drms.domain.mqtt.CommandKv;
import com.fxkj.cloud.drms.domain.mqtt.service.SenderService;
import com.fxkj.cloud.dbs.common.model.kv.TsKvEntry;
import com.fxkj.cloud.drms.domain.product.ProductRepository;
import com.fxkj.cloud.drms.domain.ts.repository.TimeseriesRepository;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>description: </p>
 * @author ww
 * @date 2025/3/18 13:45
 */
@Component
public class DeviceApplicationServiceImpl implements DeviceApplicationService {

    private final TimeseriesRepository timeseriesRepository;
    private final DeviceRepository deviceRepository;
    private final ProductRepository productRepository;
    private final SenderService senderService;

    public DeviceApplicationServiceImpl(TimeseriesRepository timeseriesRepository, DeviceRepository deviceRepository, ProductRepository productRepository, SenderService senderService) {
        this.timeseriesRepository = timeseriesRepository;
        this.deviceRepository = deviceRepository;
        this.productRepository = productRepository;
        this.senderService = senderService;
    }

    @Override
    public void createDevice(UUID organizationId, UUID productId, UUID deviceId) {
        timeseriesRepository.createSonTable(organizationId, productId, deviceId);
    }

    @Override
    public ListenableFuture<Integer> saveDeviceData(String deviceSerialNo, List<TsKvEntry> tsKvEntries) {
        DeviceBasicInfo deviceBasicInfoByDeviceSerialNo = deviceRepository.getDeviceBasicInfoByDeviceSerialNo(deviceSerialNo);
        if (null == deviceSerialNo) {
            return Futures.immediateFuture(0);
        }
        return timeseriesRepository.save(
                deviceBasicInfoByDeviceSerialNo.getOrganizationId(),
                deviceBasicInfoByDeviceSerialNo.getProductId(),
                deviceBasicInfoByDeviceSerialNo.getDeviceId(),
                tsKvEntries
        );
    }

    @Override
    public void deviceStatusChange(String deviceSerialNo, DeviceStatusEnum status) {
        deviceRepository.deviceStatusChange(deviceSerialNo, status);
    }

    @Override
    public ListenableFuture<List<TsKvEntry>> getDeviceLatestTskv(UUID deviceId, Set<String> attributeKeys) {
        List<DeviceAttributeKey> deviceAttributeKeys = deviceRepository.getDeviceAttributeKeys(deviceId, attributeKeys);
        if (null == deviceAttributeKeys || deviceAttributeKeys.isEmpty()) {
            return Futures.immediateFuture(Collections.emptyList());
        }
        return timeseriesRepository.getLatestTsKv(deviceId, deviceAttributeKeys);
    }

    @Override
    public ListenableFuture<List<DeviceAttributeKeyValue>> getDeviceLatestAttributeTskv(UUID deviceId, Set<String> attributeKeys) {
        if (null == attributeKeys || attributeKeys.isEmpty()) {
            attributeKeys = new HashSet<>();
        }
        List<DeviceAttributeKey> deviceAttributeKeys = deviceRepository.getDeviceAttributeKeys(deviceId, attributeKeys);
        if (null == deviceAttributeKeys || deviceAttributeKeys.isEmpty()) {
            return Futures.immediateFuture(Collections.emptyList());
        }

        // 获取设备最新属性数据
        ListenableFuture<List<TsKvEntry>> latestTsKv = timeseriesRepository.getLatestTsKv(deviceId, deviceAttributeKeys);

        //转换结果集
        return Futures.transform(latestTsKv, tsKvEntries -> {
            List<DeviceAttributeKeyValue> deviceAttributeKeyValues = new ArrayList<>();
            Map<String, DeviceAttributeKey> keyMap = deviceAttributeKeys.stream().collect(Collectors.toMap(key -> key.getKey(), key -> key));
            tsKvEntries.forEach(tsKvEntry -> {
                deviceAttributeKeyValues.add(new DeviceAttributeKeyValue(tsKvEntry, keyMap.get(tsKvEntry.getKey())));
            });
            return deviceAttributeKeyValues;
        }, MoreExecutors.directExecutor());
    }

    @Override
    public ListenableFuture<List<TskvView>> getDeviceHistoryTskv(UUID deviceId, Set<String> attributeKeys, long startTime, long endTime, Integer limit) {
        List<DeviceAttributeKey> deviceAttributeKeys = deviceRepository.getDeviceAttributeKeys(deviceId, attributeKeys);
        if (null == deviceAttributeKeys || deviceAttributeKeys.isEmpty()) {
            return Futures.immediateFuture(Collections.emptyList());
        }
        return Futures.transform(timeseriesRepository.getHistoryTsKv(deviceId, deviceAttributeKeys, startTime, endTime, limit), tsKvEntries -> {
            List<TskvView> tskvViews = new ArrayList<>();
            tsKvEntries.forEach(tsKvEntry -> {
                tskvViews.add(new TskvView(tsKvEntry.getTs(), tsKvEntry.getKey(), tsKvEntry.getValueAsString(), tsKvEntry.getDataType()));
            });
            return tskvViews;
        }, MoreExecutors.directExecutor());
    }

    @Override
    public void deviceControlPlanCallback(UUID requestId, String deviceSerialNo) {
        deviceRepository.deviceControlPlanCallback(requestId, deviceSerialNo);
    }

    @Override
    public void deviceKeySet(UUID deviceId, List<KvDTO> kvSettings, String username) {
        //获取属性key信息
        Set<String> attributeCodes = kvSettings.stream().map(KvDTO::attributeCode).collect(Collectors.toSet());
        //属性id和属性key信息的映射
        Map<String, KvDTO> attributeIdKvMap = kvSettings.stream().collect(Collectors.toMap(KvDTO::attributeCode, kv -> kv));
        List<DeviceAttributeKey> deviceAttributeKeys = deviceRepository.getDeviceAttributeKeys(deviceId, attributeCodes);
        if (deviceAttributeKeys.isEmpty()) {
            return;
        }
        //构建指令kv
        List<CommandKv> commandKvs = new ArrayList<>();
        deviceAttributeKeys.forEach(deviceAttributeKey -> {
            KvDTO kvDTO = attributeIdKvMap.get(deviceAttributeKey.getKey());
            if (null == kvDTO) {
                return;
            }
            commandKvs.add(new CommandKv(deviceAttributeKey, kvDTO.value()));
        });

        //获取设备信息
        DeviceBasicInfo deviceBasicInfo = deviceRepository.getDeviceBasicInfoByDeviceId(deviceId);
        if (deviceBasicInfo == null) {
            return;
        }
        //指令构建
        CommandDomain commandDomain = CommandDomain.create(deviceBasicInfo, commandKvs, username);
        //指令下发
        senderService.send(commandDomain);
    }

    @Override
    public void deviceKeySetBatch(List<UUID> deviceIds, List<KvDTO> kvSettings, String username) {
        //获取属性key信息
        Set<String> attributeCodes = kvSettings.stream().map(KvDTO::attributeCode).collect(Collectors.toSet());
        //属性id和属性key信息的映射
        Map<String, KvDTO> attributeIdKvMap = kvSettings.stream().collect(Collectors.toMap(KvDTO::attributeCode, kv -> kv));
        List<DeviceAttributeKey> deviceAttributeKeys = deviceRepository.getDeviceAttributeKeys(deviceIds.getFirst(), attributeCodes);
        if (deviceAttributeKeys.isEmpty()) {
            return;
        }
        //构建指令kv
        List<CommandKv> commandKvs = new ArrayList<>();
        deviceAttributeKeys.forEach(deviceAttributeKey -> {
            KvDTO kvDTO = attributeIdKvMap.get(deviceAttributeKey.getKey());
            if (null == kvDTO) {
                return;
            }
            commandKvs.add(new CommandKv(deviceAttributeKey, kvDTO.value()));
        });

        //获取设备信息
        Map<UUID, DeviceBasicInfo> deviceBasicInfoMap = deviceRepository.getDeviceBasicInfoByDeviceIds(new HashSet<>(deviceIds));
        List<CommandDomain> commandDomains = new ArrayList<>();
        deviceBasicInfoMap.entrySet().forEach(entry -> {
            commandDomains.add(CommandDomain.create(entry.getValue(), commandKvs, username));
        });
        senderService.sendBatch(commandDomains);
    }

    @Override
    public void commandKvSet(CommandSet commandSet, String username) {
        UUID deviceId = commandSet.deviceId();
        Integer sn = commandSet.sn();
        //默认sn为1
        if (sn == null) {
            sn = 1;
        }
        List<CommandIdValue> commands = commandSet.commands();
        Map<UUID, CommandIdValue> commandMap = commands.stream().collect(Collectors.toMap(CommandIdValue::attributeId, commandIdValue -> commandIdValue));
        Set<UUID> attributeIds = commands.stream().map(CommandIdValue::attributeId).collect(Collectors.toSet());
        //获取设备属性信息
        List<DeviceAttributeKey> deviceAttributeKeys = deviceRepository.getDeviceAttributeKeys(deviceId, sn, attributeIds);
        if (deviceAttributeKeys.isEmpty()) {
            return;
        }
        //构建指令kv
        List<CommandKv> commandKvs = new ArrayList<>();
        deviceAttributeKeys.forEach(deviceAttributeKey -> {
            commandKvs.add(new CommandKv(deviceAttributeKey, commandMap.get(deviceAttributeKey.getAttributeId()).value()));
        });

        //获取设备信息
        DeviceBasicInfo deviceBasicInfo = deviceRepository.getDeviceBasicInfoByDeviceId(deviceId);
        if (deviceBasicInfo == null) {
            return;
        }
        //指令构建
        CommandDomain commandDomain = CommandDomain.create(deviceBasicInfo, commandKvs, username);
        //指令下发
        senderService.send(commandDomain);
    }

    @Override
    public ListenableFuture<ProductStatusStatistics> getProductModelStatusStatistics(UUID organizationId, UUID productModelId, UUID productId, Long selectTime) {
        Set<UUID> productIdsByModelId = new HashSet<>();
        if (null != productModelId) {
            productIdsByModelId = productRepository.getProductIdsByModelId(productModelId);
            if (null == productIdsByModelId || productIdsByModelId.isEmpty()) {
                return Futures.immediateFuture(null);
            }
        } else {
            productIdsByModelId.add(productId);
        }
        return timeseriesRepository.getProductStatusStatistics(organizationId, productIdsByModelId, selectTime);
    }

    @Override
    public ListenableFuture<ProductDeviceStatusChangeHistory> getProductDeviceStatusChangeHistory(UUID organizationId, UUID productModelId, UUID productId, UUID deviceId, Long startTime, Long endTime, Integer intervalValue, IntervalType intervalTypeEnum) {
        Set<UUID> productIdsByModelId = new HashSet<>();
        if (null != productModelId) {
            productIdsByModelId = productRepository.getProductIdsByModelId(productModelId);
            if (null == productIdsByModelId || productIdsByModelId.isEmpty()) {
                return Futures.immediateFuture(null);
            }
        } else {
            productIdsByModelId.add(productId);
        }
        return timeseriesRepository.getProductDeviceStatusChangeHistory(organizationId, productIdsByModelId, productId, deviceId, startTime, endTime, intervalValue, intervalTypeEnum);
    }


    @Override
    public ListenableFuture<List<ProductDeviceLightStatistics>> getProductModelLightStatistics(UUID organizationId, UUID productModelId, UUID productId, Long startTime, Long endTime) {
        Set<UUID> productIds = new HashSet<>();
        if (null != productModelId) {
            productIds = productRepository.getProductIdsByModelId(productModelId);
            if (null == productIds || productIds.isEmpty()) {
                return Futures.immediateFuture(Collections.emptyList());
            }
        } else {
            productIds.add(productId);
        }
        return timeseriesRepository.getProductLightStatistics(organizationId, productIds, startTime, endTime);
    }

    @Override
    public ListenableFuture<List<BatchTskvView>> tsQuery(TsQueryDTO tsQueryDTO) {
        return Futures.transform(timeseriesRepository.tsQuery(buildTsQuery(tsQueryDTO)), new Function<List<TsQueryResult>, List<BatchTskvView>>() {
            @Override
            public List<BatchTskvView> apply(List<TsQueryResult> input) {
                List<BatchTskvView> batchTskvViews = new ArrayList<>();
                Set<UUID> deviceIds = input.stream().map(TsQueryResult::getDeviceId).filter(Objects::nonNull).collect(Collectors.toSet());
                Map<UUID, DeviceBasicInfo> deviceBasicInfoMap;
                if (!deviceIds.isEmpty()) {
                    deviceBasicInfoMap = deviceRepository.getDeviceBasicInfoByDeviceIds(deviceIds);
                    if (null != input && !input.isEmpty()) {
                        input.forEach(tsQueryResult -> {
                            batchTskvViews.add(new BatchTskvView(tsQueryResult.getDeviceId(), deviceBasicInfoMap.get(tsQueryResult.getDeviceId()).getDeviceName(), tsQueryResult.getTs(), tsQueryResult.getKvs()));
                        });
                    }
                } else {
                    if (null != input && !input.isEmpty()) {
                        input.forEach(tsQueryResult -> {
                            batchTskvViews.add(new BatchTskvView(null, null, tsQueryResult.getTs(), tsQueryResult.getKvs()));
                        });
                    }
                }

                return batchTskvViews;
            }
        }, MoreExecutors.directExecutor());
    }

    @Override
    public ListenableFuture<ProductEnergyStatisticsModelView> productModelEnergyStatistics(UUID productModelId, UUID productId, UUID organizationId) {
        Set<UUID> productIds = new HashSet<>();
        if (null != productModelId) {
            productIds = productRepository.getProductIdsByModelId(productModelId);
        } else {
            productIds.add(productId);
        }
        if (null == productIds || productIds.isEmpty()) {
            return Futures.immediateFuture(null);
        }
        return Futures.transform(productRepository.getProductEnergyStatistics(productIds, organizationId),
                input -> new ProductEnergyStatisticsModelView(
                        String.valueOf(input.getTodayEnergy()),
                        String.valueOf(input.getTodayRatedEnergy()),
                        String.valueOf(input.getTodayEnergyConservationRate()),
                        String.valueOf(input.getYesterdayEnergy()),
                        String.valueOf(input.getYesterdayRatedEnergy()),
                        String.valueOf(input.getYesterdayEnergyConservationRate()),
                        String.valueOf(input.getCmEnergy()),
                        String.valueOf(input.getCmRatedEnergy()),
                        String.valueOf(input.getCmEnergyConservationRate()),
                        String.valueOf(input.getLmEnergy()),
                        String.valueOf(input.getLmRatedEnergy()),
                        String.valueOf(input.getLmEnergyConservationRate()),
                        String.valueOf(input.getTotalEnergy()),
                        String.valueOf(input.getTotalRatedEnergy()),
                        String.valueOf(input.getTotalEnergyConservationRate())
                ), MoreExecutors.directExecutor());
    }

    private TsQuery buildTsQuery(TsQueryDTO dto) {
        QueryType queryType = QueryType.of(dto.queryType());
        if (null == queryType) {
            throw new IllegalArgumentException("查询类型非法");
        }
        if (queryType.isTypeQuery()) {
            //queryId为设备种类id
            //通过该id查询产品ids
            Set<UUID> productIdsByModelId = productRepository.getProductIdsByModelId(UUID.fromString(dto.queryId()));
            if (null == productIdsByModelId || productIdsByModelId.isEmpty()) {
                throw new IllegalArgumentException("查询设备类型id非法");
            }
            return new TsQuery(
                    QueryType.of(dto.queryType()),
                    dto.queryKeys(),
                    dto.aggregationType(),
                    dto.intervalType(),
                    dto.intervalValue(),
                    dto.startTime(),
                    dto.endTime(),
                    dto.limit(),
                    dto.offset(),
                    new ArrayList<>(productIdsByModelId),
                    dto.groupKeys(),
                    dto.sortKey(),
                    QuerySortType.of(dto.sort())
            );
        } else {
            return new TsQuery(
                    QueryType.of(dto.queryType()),
                    dto.queryKeys(),
                    dto.aggregationType(),
                    dto.intervalType(),
                    dto.intervalValue(),
                    dto.startTime(),
                    dto.endTime(),
                    dto.limit(),
                    dto.offset(),
                    dto.queryId(),
                    dto.groupKeys(),
                    dto.sortKey(),
                    QuerySortType.of(dto.sort())
            );
        }
    }


}
